#define PrynGuiInternal
#define PrynGuiImplementation
#include <prynGui.h>
#include <stdarg.h>

// External functions that need to be implemented by the platform.
/*
PrynResult prynGuiPainterBrush (PrynGuiPainter *painter, PrynGuiBrush brush);
PrynResult prynGuiPainterCreateForHwnd (PrynState *state, PrynGuiPainter **painter, void *window, void *device, void *rectangle);
void prynGuiPainterDestroy (PrynGuiPainter *painter);
PrynResult PrynExport prynGuiPainterFillRectangleColor (PrynGuiPainter *painter, int left, int top, int right, int bottom, PrynColor color);
PrynResult prynGuiPainterRectangle (PrynGuiPainter *painter, int left, int top, int right, int bottom);
PrynResult prynGuiPainterPen (PrynGuiPainter *painter, PrynGuiPen pen);
PrynResult prynGuiPainterPrint (PrynGuiPainter *painter, int x, int y, const PrynString string);
PrynResult prynGuiPainterTextAlign (PrynGuiPainter *painter, PrynGuiTextAlign align);
PrynResult prynGuiPainterClip (PrynGuiPainter *painter, int left, int top, int right, int bottom);
PrynResult prynGuiPainterClipReset (PrynGuiPainter *painter);
PrynResult prynGuiPainterPrintArea (PrynGuiPainter *painter, int left, int top, int right, int bottom, const PrynString string);
PrynResult prynGuiPainterTextFits (PrynGuiPainter *painter, const PrynString string, int width, int *result);
PrynResult prynGuiPainterLineData (PrynGuiPainter *painter, int pointCount, int *points);
PrynResult PrynExport prynGuiPainterCircle (PrynGuiPainter *painter, int centerX, int centerY, int radius);
*/

PrynGuiExport (PrynResult, prynGuiPainterState, (PrynGuiPainter *painter, PrynState **output)) 
{
	if (output) *output = 0;
	if (!painter) return PrynResultNullArgument;
	if (output) *output = painter->pState;
	return PrynResultSuccess;
}

PrynGuiExport (PrynResult, prynGuiPainterClear, (PrynGuiPainter *painter, PrynColor color))
{
	if (!painter) return PrynResultNullArgument;
	return prynGuiPainterFillRectangleColor (painter, painter->pLeft, painter->pTop, painter->pRight, painter->pBottom, color);
}

PrynGuiExport (PrynResult, prynGuiPainterSetBrushNone, (PrynGuiPainter *painter))
{
	PrynGuiBrush brush;
	
	brush.type = PrynGuiBrushTypeNone;
	return prynGuiPainterSetBrush (painter, &brush);
}

PrynGuiExport (PrynResult, prynGuiPainterSetBrushColor, (PrynGuiPainter *painter, PrynColor color))
{
	PrynGuiBrush brush;
	
	brush.type = PrynGuiBrushTypeColor;
	brush.value.color = color;
	return prynGuiPainterSetBrush (painter, &brush);
}

PrynGuiExport (PrynResult, prynGuiPainterSetPenNone, (PrynGuiPainter *painter))
{
	PrynGuiPen pen;
	
	pen.type = PrynGuiPenTypeNone;
	pen.repeat = 0;
	return prynGuiPainterSetPen (painter, &pen);
}

PrynGuiExport (PrynResult, prynGuiPainterSetPenSolid, (PrynGuiPainter *painter, int width, PrynColor color))
{
	PrynGuiPen pen;
	
	pen.type = PrynGuiPenTypeSolid;
	pen.color = color;
	pen.width = width;
	pen.repeat = 0;
	return prynGuiPainterSetPen (painter, &pen);
}

PrynGuiExport (PrynResult, prynGuiPainterSetPenPattern, (PrynGuiPainter *painter, int width, PrynColor fallback, int patternWidth, int patternHeight, PrynColor *pattern, int offset, PrynGuiPenRepeat repeat))
{
	PrynGuiPen pen;
	
	pen.type = PrynGuiPenTypePattern;
	pen.color = fallback;
	pen.width = width;
	pen.imageWidth = patternWidth;
	pen.imageHeight = patternHeight;
	pen.image = pattern;
	pen.offset = offset;
	pen.repeat = repeat;
	return prynGuiPainterSetPen (painter, &pen);
}

bool isSpace (char value)
{
	return value == ' ' || value == '\n' || value == '\r' || value == '\t' || value == '\v';
}

PrynGuiExport (PrynResult, prynGuiPainterWordFits, (PrynGuiPainter *painter, const PrynString *string, int width, size_t *output))
{
	PrynResult result = prynGuiPainterTextFits (painter, string, width, output); // Get where the line splits in characters.
	if (result < 0) return result; // Reported error so relay it.
	if (*output == string->length) return result; // Handles null strings as well.
	while (*output > 0 && !isSpace (string->pointer [*output])) (*output) --; // Round down to previous word.
	if (*output == 0) while (*output < string->length && !isSpace (string->pointer [*output])) (*output) ++; // Don't return zero, round up even though it goes over.
	return result;
}

PrynGuiExport (PrynResult, prynGuiPainterPrintArea, (PrynGuiPainter *painter, int left, int top, int right, int bottom, const PrynString *textBase))
{
	PrynString text;
	PrynResult result;
	int width = right - left, advance;
	PrynGuiTextAlign align = 0;
	int x = left, y = top;
	PrynGuiTextAlign textAlign = 0;
	int lines;

	if (!painter || !painter->pPlatform || !textBase) return PrynResultNullArgument;
	text = *textBase;
	
	prynGuiPainterTextAlign (painter, &align);
	prynGuiPainterTextAdvance (painter, &advance);
	
	if ((align & PrynGuiTextAlignVerticalMask) != PrynGuiTextAlignTop)
	{
		if ((result = prynGuiPainterCountTextLines (painter, &text, width, &lines)) < 0)
			goto failure;
	}
	
	switch (align & PrynGuiTextAlignHorizontalMask)
	{
		case PrynGuiTextAlignRight: textAlign |= PrynGuiTextAlignRight; x = right; break;
		case PrynGuiTextAlignCenter: textAlign |= PrynGuiTextAlignCenter; x = (left + right) / 2; break;
		case PrynGuiTextAlignLeft: default: textAlign |= PrynGuiTextAlignLeft; break;
	}
	
	switch (align & PrynGuiTextAlignVerticalMask)
	{
		case PrynGuiTextAlignTop: default: case PrynGuiTextAlignBaseLine: y = top; break;
		case PrynGuiTextAlignBottom: y = bottom - advance * lines; break;
	}
	
	if ((result = prynGuiPainterSetTextAlign (painter, textAlign)) < 0) goto failure;
	
	while (text.length)
	{
		PrynString line;
		int sizeX, sizeY;
		size_t count;
		
		if ((result = prynGuiPainterWordFits (painter, &text, width, &count)) < 0)
			goto failure;
		prynStringSliceTo (&line, &text, count);
		if ((result = prynGuiPainterMeasureText (painter, &line, &sizeX, &sizeY)) < 0
			|| (result = prynGuiPainterPrint (painter, x, y, &line)) < 0)
			goto failure;
		y += advance;
		while (count < text.length && isSpace (text.pointer [count]))
			count ++;
		prynStringSliceToEnd (&text, &text, count);
	}
	
success:
	if (0) { failure:;
	}
	
	prynGuiPainterSetTextAlign (painter, align);
	return result;
}

PrynGuiExport (PrynResult, prynGuiPainterCountTextLines, (PrynGuiPainter *painter, const PrynString *textBase, int width, int *output))
{
	PrynString text;
	PrynResult result = 0;

	if (!painter || !textBase || !output) return PrynResultNullArgument;
	text = *textBase;
	
	for (*output = 0; text.length; (*output) ++)
	{
		size_t count;
		
		if ((result = prynGuiPainterWordFits (painter, &text, width, &count)) < 0)
			return result;
		while (count < text.length && isSpace (text.pointer [count]))
			count ++;
		prynStringSliceToEnd (&text, &text, count);
	}
	
	return result;
}

PrynGuiExportVariadic (PrynResult, prynGuiPainterLine, (PrynGuiPainter *painter, int pointCount, ...))
{
	va_list points;
	PrynResult result;
	
	va_start (points, pointCount);
	result = prynGuiPainterLineData (painter, pointCount, (int *) points);
	va_end (points);
	
	return result;
}


